package org.dromara.common.mybatis.core.mapper

import cn.hutool.core.collection.CollUtil
import com.baomidou.mybatisplus.core.conditions.Wrapper
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper
import com.baomidou.mybatisplus.core.mapper.BaseMapper
import com.baomidou.mybatisplus.core.metadata.IPage
import com.baomidou.mybatisplus.core.toolkit.reflect.GenericTypeUtils
import com.baomidou.mybatisplus.extension.plugins.pagination.Page
import com.baomidou.mybatisplus.extension.toolkit.Db
import org.apache.ibatis.logging.Log
import org.apache.ibatis.logging.LogFactory
import org.dromara.common.core.utils.MapstructUtils.convert
import org.dromara.common.core.utils.StreamUtils.toList
import java.io.Serializable
import java.util.function.Function

/**
 * 自定义 Mapper 接口, 实现 自定义扩展
 *
 * @param <T> table 泛型
 * @param <V> vo 泛型
 * @author Lion Li
 * @updater LikeYouDo
 * @date 2025/1/26 1:34
 */
@Suppress("UNCHECKED_CAST")
interface BaseMapperPlus<T, V> : BaseMapper<T> {

    val log: Log
        get() = LogFactory.getLog(BaseMapperPlus::class.java)

    /**
     * 获取当前实例对象关联的泛型类型 V 的 Class 对象
     *
     * @return 返回当前实例对象关联的泛型类型 V 的 Class 对象
     */
    fun currentVoClass(): Class<V> {
        return GenericTypeUtils.resolveTypeArguments(
            this.javaClass,
            BaseMapperPlus::class.java
        )[1] as Class<V>
    }

    /**
     * 获取当前实例对象关联的泛型类型 T 的 Class 对象
     *
     * @return 返回当前实例对象关联的泛型类型 T 的 Class 对象
     */
    fun currentModelClass(): Class<T> {
        return GenericTypeUtils.resolveTypeArguments(
            this.javaClass,
            BaseMapperPlus::class.java
        )[0] as Class<T>
    }

    /**
     * 使用默认的查询条件查询并返回结果列表
     *
     * @return 返回查询结果的列表
     */
    fun selectList(): List<T> {
        return this.selectList(QueryWrapper())
    }

    /**
     * 批量插入实体对象集合
     *
     * @param entityList 实体对象集合
     * @return 插入操作是否成功的布尔值
     */
    fun insertBatch(entityList: Collection<T>?): Boolean {
        return Db.saveBatch(entityList)
    }

    /**
     * 批量根据ID更新实体对象集合
     *
     * @param entityList 实体对象集合
     * @return 更新操作是否成功的布尔值
     */
    fun updateBatchById(entityList: Collection<T>?): Boolean {
        return Db.updateBatchById(entityList)
    }

    /**
     * 批量插入或更新实体对象集合
     *
     * @param entityList 实体对象集合
     * @return 插入或更新操作是否成功的布尔值
     */
    fun insertOrUpdateBatch(entityList: Collection<T>?): Boolean {
        return Db.saveOrUpdateBatch(entityList)
    }

    /**
     * 批量插入实体对象集合并指定批处理大小
     *
     * @param entityList 实体对象集合
     * @param batchSize  批处理大小
     * @return 插入操作是否成功的布尔值
     */
    fun insertBatch(entityList: Collection<T>?, batchSize: Int): Boolean {
        return Db.saveBatch(entityList, batchSize)
    }

    /**
     * 批量根据ID更新实体对象集合并指定批处理大小
     *
     * @param entityList 实体对象集合
     * @param batchSize  批处理大小
     * @return 更新操作是否成功的布尔值
     */
    fun updateBatchById(entityList: Collection<T>?, batchSize: Int): Boolean {
        return Db.updateBatchById(entityList, batchSize)
    }

    /**
     * 批量插入或更新实体对象集合并指定批处理大小
     *
     * @param entityList 实体对象集合
     * @param batchSize  批处理大小
     * @return 插入或更新操作是否成功的布尔值
     */
    fun insertOrUpdateBatch(entityList: Collection<T>?, batchSize: Int): Boolean {
        return Db.saveOrUpdateBatch(entityList, batchSize)
    }

    /**
     * 根据ID查询单个VO对象
     *
     * @param id 主键ID
     * @return 查询到的单个VO对象
     */
    fun selectVoById(id: Serializable): V? {
        return selectVoById(id, this.currentVoClass())
    }

    /**
     * 根据ID查询单个VO对象并将其转换为指定的VO类
     *
     * @param id      主键ID
     * @param voClass 要转换的VO类的Class对象
     * @param <C>     VO类的类型
     * @return 查询到的单个VO对象，经过转换为指定的VO类后返回
     */
    fun <C> selectVoById(id: Serializable, voClass: Class<C>): C? {
        val obj = this.selectById(id)
        if (obj === null) {
            return null
        }
        return convert<T, C>(obj, voClass)
    }

    /**
     * 根据ID集合批量查询VO对象列表
     *
     * @param idList 主键ID集合
     * @return 查询到的VO对象列表
     */
    fun selectVoByIds(idList: Collection<Serializable>): List<V>? {
        return selectVoByIds(idList, this.currentVoClass())
    }

    /**
     * 根据ID集合批量查询实体对象列表，并将其转换为指定的VO对象列表
     *
     * @param idList  主键ID集合
     * @param voClass 要转换的VO类的Class对象
     * @param <C>     VO类的类型
     * @return 查询到的VO对象列表，经过转换为指定的VO类后返回
     */
    fun <C> selectVoByIds(idList: Collection<Serializable>, voClass: Class<C>): List<C>? {
        val list = this.selectByIds(idList)
        if (CollUtil.isEmpty(list)) {
            return CollUtil.newArrayList()
        }
        return convert(list, voClass)
    }

    /**
     * 根据查询条件Map查询VO对象列表
     *
     * @param map 查询条件Map
     * @return 查询到的VO对象列表
     */
    fun selectVoByMap(map: Map<String, Any?>): List<V>? {
        return selectVoByMap(map, this.currentVoClass())
    }

    /**
     * 根据查询条件Map查询实体对象列表，并将其转换为指定的VO对象列表
     *
     * @param map     查询条件Map
     * @param voClass 要转换的VO类的Class对象
     * @param <C>     VO类的类型
     * @return 查询到的VO对象列表，经过转换为指定的VO类后返回
     */
    fun <C> selectVoByMap(map: Map<String, Any?>, voClass: Class<C>): List<C>? {
        val list = this.selectByMap(map)
        if (CollUtil.isEmpty(list)) {
            return CollUtil.newArrayList()
        }
        return convert(list, voClass)
    }

    /**
     * 根据条件查询单个VO对象
     *
     * @param wrapper 查询条件Wrapper
     * @return 查询到的单个VO对象
     */
    fun selectVoOne(wrapper: Wrapper<T>): V? {
        return selectVoOne(wrapper, this.currentVoClass())
    }

    /**
     * 根据条件查询单个VO对象，并根据需要决定是否抛出异常
     *
     * @param wrapper 查询条件Wrapper
     * @param throwEx 是否抛出异常的标志
     * @return 查询到的单个VO对象
     */
    fun selectVoOne(wrapper: Wrapper<T>, throwEx: Boolean): V? {
        return selectVoOne(wrapper, this.currentVoClass(), throwEx)
    }

    /**
     * 根据条件查询单个VO对象，并指定返回的VO对象的类型
     *
     * @param wrapper 查询条件Wrapper
     * @param voClass 返回的VO对象的Class对象
     * @param <C>     返回的VO对象的类型
     * @return 查询到的单个VO对象，经过类型转换为指定的VO类后返回
     */
    fun <C> selectVoOne(wrapper: Wrapper<T>, voClass: Class<C>): C? {
        return selectVoOne(wrapper, voClass, true)
    }

    /**
     * 根据条件查询单个实体对象，并将其转换为指定的VO对象
     *
     * @param wrapper 查询条件Wrapper
     * @param voClass 要转换的VO类的Class对象
     * @param throwEx 是否抛出异常的标志
     * @param <C>     VO类的类型
     * @return 查询到的单个VO对象，经过转换为指定的VO类后返回
     */
    fun <C> selectVoOne(wrapper: Wrapper<T>, voClass: Class<C>, throwEx: Boolean): C? {
        val obj = this.selectOne(wrapper, throwEx)
        if (obj === null) {
            return null
        }
        return convert<T, C>(obj, voClass)
    }

    /**
     * 查询所有VO对象列表
     *
     * @return 查询到的VO对象列表
     */
    fun selectVoList(): List<V>? {
        return selectVoList(QueryWrapper(), this.currentVoClass())
    }

    /**
     * 根据条件查询VO对象列表
     *
     * @param wrapper 查询条件Wrapper
     * @return 查询到的VO对象列表
     */
    fun selectVoList(wrapper: Wrapper<T>): List<V>? {
        return selectVoList(wrapper, this.currentVoClass())
    }

    /**
     * 根据条件查询实体对象列表，并将其转换为指定的VO对象列表
     *
     * @param wrapper 查询条件Wrapper
     * @param voClass 要转换的VO类的Class对象
     * @param <C>     VO类的类型
     * @return 查询到的VO对象列表，经过转换为指定的VO类后返回
     */
    fun <C> selectVoList(wrapper: Wrapper<T>, voClass: Class<C>): List<C> {
        val list = this.selectList(wrapper)
        if (CollUtil.isEmpty(list)) {
            return CollUtil.newArrayList()
        }
        return convert(list, voClass)
    }

    /**
     * 根据条件分页查询VO对象列表
     *
     * @param page    分页信息
     * @param wrapper 查询条件Wrapper
     * @return 查询到的VO对象分页列表
     */
    fun <P : IPage<V>> selectVoPage(page: IPage<T>, wrapper: Wrapper<T>): P? {
        return selectVoPage(page, wrapper, this.currentVoClass())
    }

    /**
     * 根据条件分页查询实体对象列表，并将其转换为指定的VO对象分页列表
     *
     * @param page    分页信息
     * @param wrapper 查询条件Wrapper
     * @param voClass 要转换的VO类的Class对象
     * @param <C>     VO类的类型
     * @param <P>     VO对象分页列表的类型
     * @return 查询到的VO对象分页列表，经过转换为指定的VO类后返回
     */
    fun <C, P : IPage<C>> selectVoPage(page: IPage<T>, wrapper: Wrapper<T>, voClass: Class<C>): P? {
        // 根据条件分页查询实体对象列表
        val list = this.selectList(page, wrapper)
        // 创建一个新的VO对象分页列表，并设置分页信息
        val voPage: IPage<C> = Page(page.current, page.size, page.total)
        if (CollUtil.isEmpty(list)) {
            return voPage as P
        }
        voPage.setRecords(convert(list, voClass))
        return voPage as P
    }

    /**
     * 根据条件查询符合条件的对象，并将其转换为指定类型的对象列表
     *
     * @param wrapper 查询条件Wrapper
     * @param mapper  转换函数，用于将查询到的对象转换为指定类型的对象
     * @param <C>     要转换的对象的类型
     * @return 查询到的符合条件的对象列表，经过转换为指定类型的对象后返回
     */
    fun <C> selectObjs(wrapper: Wrapper<T>, mapper: Function<in Any?, C>): List<C>? {
        return toList(this.selectObjs(wrapper), mapper)
    }
}
